          PH.ARGS DRPT,WORK.ID,OPT,VALUS,MASKS,SS.MASKS,WORK.ITEM,SSEL
** Version# 42.0003[5] - 05/17/2017 - 02:32pm - TSMITH - eclipse
*** V42.0003 Change - Custom Coding . - 05/17/2017 - TSMITH - eclipse
*** V42.0002 Change - Custom Coding . - 01/18/2017 - TSMITH - eclipse
*** V42.0001 Change - Custom Coding . - 04/01/2016 - TSMITH - eclipse

*** Routine: REP.PHR.WRITER2
*-------------------------------------------------------------------------*
*** Phantom routine that actually prints out the actual report. (From
*** report writer)
*-------------------------------------------------------------------------*
*** DRPT [IN]      - Actual Hold Report created                       (OUT)
*** WORK.ID [IN]   - The Report Writer Work Id                        (IN)
*** OPT [IN]       - Report Options                                   (IN)
*** VALUS [IN]     - Specific Branch Data                             (IN)
*** MASKS [IN]     - Conversions for values output by dict items      (IN)
*** SS.MASKS [IN]  - (IN)
*** WORK.ITEM [IN] - The WORK record                                  (IN)
*** SSEL [IN]      - The Sort Selects                                 (IN)
*-------------------------------------------------------------------------*
*** COMMON USED:
*-------------------------------------------------------------------------*
*** Multi break with null values fix MODS FOR LABELS
*-------------------------------------------------------------------------*
*** ODBC Logic - the ODBC.FILE flag is true whenever and ODBC file will be
*** written. COLUMN.OUT is true whenever the Solar Add On is authorized.
*** If the report is being run without the Add On just to be printed,
*** COLUMN.OUT OR NOT(ODBC.FILE) logic will be followed, generating a
*** printed report. If ODBC.FILE is initially true, data is written to an
*** ODBC.FILE with the passed in name. If COLUMN.OUT is also true, a
*** report will be printed in addition to the file creation. However, if
*** COLUMN.OUT is true but ODBC.FILE is not, an ODBC.FILE will -still- be
*** created using the name ODBC.<PID>. ODBC data is written in internal
*** (unconverted) format, and includes only the lowest level detail or
*** summary lines (no totaling).
*-------------------------------------------------------------------------*
*** Check for Solar Reporter Add On
          UT.SEC3 30,AUTH.OK,,NO
          IF AUTH.OK THEN COLUMN.OUT = YES ELSE COLUMN.OUT = NO

          GOSUB INIT

*** Setup the common array DICT.COMMON which dictionaries can then access
          GOSUB GET.DICT

*** Select the IDs from the specified file using the specified select
*** statement to run the report for.
          GOSUB PERFORM.SELECT

*** Printout labels if that's what the user specified.
          DICT.COMMON = SAVE.DICT.COMMON
          IF PRTLBL AND (NOT(ODBC.FILE) OR COLUMN.OUT) THEN
             GOSUB PRT.LBLS
             STOP
          END

*** Setup the heading at the top of the report - If this is an ODBC data
*** source then create the file and dictionaries
          GOSUB SET.HEADER

*** Get a list of records we should evaluate each dictionary item on.
          GOSUB GET.LST

          CUR.LINE = 1
*-------------------------------------------------------------------------*
        LOOP
          READNEXT ID FROM 7 ELSE EXIT

          LINE.CT += 1
          @NI = LINE.CT
          ICNT+=1
          PRE.VAL    = ''
          VAL.STRING = ''
          VAL.STRING.ODBC = ''
          ODBC.REC   = ''
          BRK.ON     = ''
          BRK.INDX   = 0
          LNS        = ''
*-------------------------------------------------------------------------*
*--Pass One - Get nonformula values - *
          READ UDATA FROM MAINFILE,ID THEN
             FOR X = 1 TO VAL.NUM
                DICT.ID = DICTS<1,X>
                DICT.COMMON = RAISE(DICT.COM<X>)
                IF FORMULAS<1,X>='' THEN
                   PINFO = RAISE(RAISE(REM.PATH<1,X>))
                   GET.DICT.VALUE.REMOTE DICT.ID,FLNM,ID,UDATA,PINFO,DICT.VAL,FORM.ERR
                   CONVERT AM TO VM IN DICT.VAL
                   PRE.VAL<X> = DICT.VAL
                END
             NEXT X
*-------------------------------------------------------------------------*
*--Pass Two - Get formula values - *
             FOR VN = 1 TO VAL.NUM
                IF FORMULAS<1,VN>#'' THEN
                   GOSUB CALC.RSLT
                   PRE.VAL<VN> = RSLT
                END
             NEXT VN
*-------------------------------------------------------------------------*
*--Pass Three - Format String - *
             FOR X = 1 TO VAL.NUM
                DICT.VAL = RAISE(PRE.VAL<X>)
                IF BREAKONS<1,X> AND (NOT(ODBC.FILE) OR COLUMN.OUT) THEN
                   BRK.INDX += 1
                   BRK.ON<BRK.INDX> = DICT.VAL
                END

*  Wrapping Routine... Need work...
                AM.CT = DCOUNT(DICT.VAL,AM)
                FOR AMN = 1 TO AM.CT
                   CONVERT SVM TO VM IN DICT.VAL<AMN>
                   TMP = LOWER(DICT.VAL<AMN>)
                   VAL.STRING<X,AMN,-1> = TMP
                   VAL.STRING.ODBC<X,AMN,-1> = TMP
                   TMP.CNT = DCOUNT(TMP,SVM)
                   IF TMP.CNT > LNS<2,AMN> THEN LNS<2,AMN> = TMP.CNT ELSE
                      IF ODBC.FILE THEN
                         ST.CT    = TMP.CNT + 1
                         FOR NPOS = ST.CT TO LNS<2,AMN>
                            VAL.STRING.ODBC<X,AMN,NPOS> = VAL.STRING.ODBC<X,AMN,TMP.CNT>
                         NEXT NPOS
                      END
                   END
                NEXT AMN

                IF AM.CT > LNS<1> THEN LNS<1> = AM.CT ELSE
                   IF AM.CT > 0 AND ODBC.FILE THEN
                      ST.CT    = AM.CT+1
                      FOR NPOS = ST.CT TO LNS<1>
                         VAL.STRING.ODBC<X,NPOS> = VAL.STRING.ODBC<X,AM.CT>
                      NEXT NPOS
                   END
                END

                IF TOTLS<1,X> = '1' OR TOTLS<1,X> = '2' THEN
                   CONVERT "," TO "" IN DICT.VAL
                   TOTS<1,X>  += SUMMATION(DICT.VAL)
                END
             NEXT X

             IF BRK.CNT AND BRK.ON # PBK AND (NOT(DETAIL) OR NOT(ODBC.FILE) OR COLUMN.OUT) THEN
                IF PBK # '@@' THEN GOSUB BREAKON.RTN
                PBK = BRK.ON
             END
             IF TOT.CNT THEN
                BRK.TOT = ADDS(BRK.TOT,TOTS)
                TOTS    = ''
             END
*** Detail logic for printed and/or ODBC file - data string splits after
*** conversions so that ODBC data is maintained in internal format
             IF DETAIL THEN
                FOR AMV = 1 TO LNS<1>
                   IF (LNS<2,AMV>+0) < 1 THEN LNS<2,AMV> = 1
                   FOR VMV = 1 TO LNS<2,AMV>
                      OP = ''
                      FOR ATTN = 1 TO VAL.NUM
                         FMT = FMTS<1,ATTN>
                         IF VAL.STRING<ATTN,AMV,VMV> # '' THEN
                            IF DICT.FMTS<1,ATTN> THEN
                               TVAL = VAL.STRING<ATTN,AMV,VMV>
                               TVAL.ODBC = VAL.STRING.ODBC<ATTN,AMV,VMV>
                               DICT.CONV = RAISE(DICT.FMTS<1,ATTN>)
                               CONV.CT = DCOUNT(DICT.CONV,VM)
                               IF DCONV<1,ATTN> THEN
                                  TVAL = OCONV(TVAL,DCONV<1,ATTN>)
                                  TVAL.ODBC = OCONV(TVAL.ODBC,DCONV<1,ATTN>)
                                  TVAL = ICONV(TVAL,DICT.CONV<1,CONV.CT>)
                                  TVAL.ODBC = ICONV(TVAL.ODBC,DICT.CONV<1,CONV.CT>)
                               END

                               GOSUB CHK.CONV
                               VAL.STRING<ATTN,AMV,VMV> = TVAL
                               VAL.STRING.ODBC<ATTN,AMV,VMV> = TVAL.ODBC
                            END
                         END

                         IF CDLM THEN
                            CONVERT CDLM TO '' IN VAL.STRING<ATTN,AMV,VMV>
                            CONVERT CDLM TO '' IN VAL.STRING.ODBC<ATTN,AMV,VMV>
                         END

                         TMP = VAL.STRING<ATTN,AMV,VMV> FMT
                         IF ATTN # VAL.NUM AND WIDTHS<1,ATTN> THEN
                            **If col delimiter not space need to trim first
                            IF TRIM.BLKS AND CLMSPC#' ' THEN TMP=TRIM(TMP)
                            TMP = TMP:CLMSPC
                         END

                         IF TRIM.BLKS THEN TMP = TRIM(TMP)
                         IF WIDTHS<1,ATTN> THEN
                            OP = OP:TMP
                         END
                         IF ODBC.FILE THEN
                            THIS.VAL       = VAL.STRING.ODBC<ATTN,AMV,VMV>
                            ODBC.REC<ATTN> = THIS.VAL
                         END
                      NEXT ATTN

                      IF ODBC.FILE THEN
                         IF COLUMN.OUT THEN GOSUB PRT.LINE
                         GOSUB WRT.REC
                      END ELSE
                         GOSUB PRT.LINE
                      END
                   NEXT VMV
                NEXT AMV

                IF SPCS AND (NOT(ODBC.FILE) OR COLUMN.OUT) THEN
                   FOR SPC = 1 TO SPCS
                      PRINT
                   NEXT SPC
                END
             END
          END
        REPEAT

          IF LINE.CT = 0 AND (NOT(ODBC.FILE) OR COLUMN.OUT) THEN
             PRINT "No items selected"
          END ELSE
             LAST.PAGE = YES
             IF (TOT.CNT OR BRK.CNT) AND (NOT(DETAIL) OR NOT(ODBC.FILE) OR COLUMN.OUT) THEN
                IF BRK.CNT THEN GOSUB BREAKON.RTN
                IF NOT(BRK.CNT) THEN
                   GTOL = ADDS(GTOL,BRK.TOT)
                   END
                TTOL = GTOL
                PBK  = ''
                PRINT TOTLN
                GOSUB PRINT.TOT
             END
          END

          IF NOT(ODBC.FILE) OR COLUMN.OUT THEN
             PRINTER.OFF DOC.ID
          END

FINISH:
          IF NOT(ODBC.FILE) OR COLUMN.OUT THEN
             IF COLUMN.OUT THEN
                READU REP.REC FROM RPTFILE,DOC.ID ELSE REP.REC = ''
                GOSUB GET.SBY.INFO
                CONVERT '*' TO '' IN TOTLS
                REP.REC<40> = YES
                REP.REC<41> = FMTS
                REP.REC<42> = TOTLS
                REP.REC<43> = BREAKONS
                REP.REC<44> = DICT.FMTS
                REP.REC<45> = ODBC.HDRS
                REP.REC<46> = LOWER(OLD.DICT.COMMON)
                REP.REC<47> = SORT.BY.INFO
                REP.REC<48> = FORMULAS
                REP.REC<50> = LOWER(WORK.ITEM)
                WRITE REP.REC ON RPTFILE,DOC.ID
             END
          END

          UT.PH.CLEANUP
          EXECUTE 'DELETE-LIST ':SAVELIST.ID:'~':LC:PID$ CAPTURING DMY

          IF ICNT = 0 THEN
             SPOOLER.DELETE DOC.ID
             SEND.MESSAGE 'Phantom',USER.ID,TITLE:' Selected 0 items...No Report Produced'
          END ELSE
             SEND.MESSAGE 'Phantom',USER.ID,TITLE:' is Complete'
          END
          STOP
*-------------------------------------------------------------------------*
GET.SBY.INFO: *** Get the actual columns on the report they sorted by
          SORT.BY.INFO = ''
          SORT.CT      = DCOUNT(SRT.DATA<1>,VM)

          FOR SORTN = 1 TO SORT.CT
             SORT.BY.DICT = SRT.DATA<1,SORTN,1>
             LOCATE SORT.BY.DICT IN DICTS<1> SETTING SORT.LN ELSE NULL
             SORT.BY.INFO<1,SORTN,1> = SORT.LN
             SORT.BY.INFO<1,SORTN,2> = SRT.DATA<1,SORTN,2>
          NEXT SORTN

          RETURN
*-------------------------------------------------------------------------*
WRT.REC:  *** Come here once the new ODBC record is defined and write it to
          *** the newly created ODBC file.
          GET.NEW.ID ODBC.FILE:':NEW.ID',REC.ID,,,ODBCFILE

          IF LEN(REC.ID) < 10 THEN
             REC.ID = REC.ID "R%10"
          END

          WRITE ODBC.REC ON ODBCFILE,REC.ID

          RETURN
*-------------------------------------------------------------------------*
PRT.LINE: *** Print this one line of detailed information
          IF PRT.LINE AND VMV = 1 AND AMV = 1 THEN
             TMP.FMT = "R#":PRT.LINE
             PRINT CUR.LINE TMP.FMT:' ':
             CUR.LINE += 1
          END

          PRINT OP:
          IF RDLM THEN PRINT RDLM: ELSE PRINT

          RETURN
*-------------------------------------------------------------------------*
BREAKON.RTN: *
          BRK.TOL<BRK.CNT> = BRK.TOT
          PAGE.BRK = NO
          FOR P = 1 TO BRK.CNT
             IF LAST.PAGE OR BRK.ON<P> NE PBK<P> THEN
                ODBC.REC = ''
                FOR BLVL = BRK.CNT TO P STEP -1
                   BRK.COL  = BRK.POS<1,BLVL>
                   TTOL = BRK.TOL<BLVL>
*** ODBC.TOT.FLAG set to yes if this is a base summary line NOT totaled
                   IF DETAIL OR BLVL # BRK.CNT THEN
                      IF NOT(ODBC.FILE) OR COLUMN.OUT THEN PRINT TOTLN
                   END ELSE
                      IF ODBC.FILE THEN ODBC.TOT.FLAG = YES
                   END
                   GOSUB PRINT.TOT
                   PLVL = BLVL - 1
                   IF PLVL NE 0 THEN
                      BRK.TOL<PLVL> = ADDS(BRK.TOL<PLVL>,BRK.TOL<BLVL>)
                   END ELSE
                      GTOL = ADDS(GTOL,BRK.TOL<1>)
                   END
                   BRK.TOL<BLVL> = ''
                   IF NOT(LAST.PAGE) THEN
                      IF BREAKONS<1,BRK.COL> = 'P' THEN PAGE.BRK = YES
                   END
                NEXT BLVL

                IF ODBC.REC THEN GOSUB WRT.REC
                IF PAGE.BRK AND (NOT(ODBC.FILE) OR COLUMN.OUT) THEN PRINT CHAR(12)
                BRK.TOT = ''
                RETURN
             END
          NEXT P

          RETURN
*-------------------------------------------------------------------------*
CALC.RSLT:  *
          FRML.STRG = FORMULAS<1,VN>
          FVALS=DCOUNT(FRML.STRG,SVM)
          FOR FVAL = 1 TO FVALS
             IF NUM(FRML.STRG<1,1,FVAL>) THEN
                COL.INDX = FRML.STRG<1,1,FVAL>
                TVAL = PRE.VAL<COL.INDX>
                IF DCONV<1,COL.INDX>#'' THEN TVAL=OCONV(TVAL,DCONV<1,COL.INDX>)
                FRML.STRG<1,1,FVAL> = TVAL
             END
          NEXT FVAL

          CONVERT SVM TO '' IN FRML.STRG
          GET.RESULT FRML.STRG,RSLT
          IF DICT.FMTS<1,VN> # '' THEN
             RSLT = ICONV(RSLT,DICT.FMTS<1,VN>)
          END

          RETURN
*-------------------------------------------------------------------------*
PRINT.TOT: *
          TOT = ''
          TP  = 0
          PRE.VAL = RAISE(TTOL)
          FOR VN = 1 TO VAL.NUM
             OBRKVL = ''
             FMT = FMTS<1,VN>

             IF TOTLS<1,VN> = '1' OR TOTLS<1,VN> = '2' THEN
                TP = TP + 1
                IF FORMULAS<1,VN> = '' OR TOTLS<1,VN> = '1' THEN
                   OBRK.TOT = TTOL<1,VN>
                   IF DCONV<1,VN> THEN
                      OBRK.TOT = OCONV(OBRK.TOT,DCONV<1,VN>)
                      OBRK.TOT = ICONV(OBRK.TOT,DICT.FMTS<1,VN>)
                   END
                END ELSE
                   GOSUB CALC.RSLT
                   OBRK.TOT = RSLT
                END
                IF DICT.FMTS<1,VN> THEN
                   OBRK.TOT = OCONV(OBRK.TOT,DICT.FMTS<1,VN>)
                END
                IF WIDTHS<1,VN> THEN TOT = TOT:OBRK.TOT FMT:CLMSPC

                IF ODBC.TOT.FLAG THEN
                   IF DICT.FMTS<1,VN> THEN
                     * Store the ODBC value in internal format, otherwise
                     * when viewing the report from Solar the decimal point
                     * will be in the wrong place.
                     ODBC.REC<VN> = ICONV(OBRK.TOT,DICT.FMTS<1,VN>)
                   END ELSE
                     ODBC.REC<VN> = OBRK.TOT
                   END
                END

             END ELSE
                IF BRK.COL = VN AND PBK THEN
                   IF DETAIL THEN
                      IF WIDTHS<1,VN> THEN TOT = TOT:STR('*',WIDTHS<1,VN>):CLMSPC
                   END ELSE
*** Grab ODBC value before conversions
                      OBRK.ODBC = PBK<BLVL>
                      IF DICT.FMTS<1,VN> THEN
                         TVAL = PBK<BLVL>
                         IF DCONV<1,VN> THEN
                            TVAL = OCONV(TVAL,DCONV<1,VN>)
                            TVAL = ICONV(TVAL,DICT.FMTS<1,VN>)
                         END
                         PBK<BLVL> = OCONV(TVAL,DICT.FMTS<1,VN>)
                      END
                      OBRKVL = PBK<BLVL> FMT
                      IF WIDTHS<1,VN> THEN TOT = TOT:OBRKVL FMT:CLMSPC

*** If this field is for this line, add it to the ODBC rec. Also add
*** fields for other lines if they are not null. This way the "one-line"
*** ODBC rec will have the field data from ALL of the printed sum lines
                      IF ODBC.TOT.FLAG THEN ODBC.REC<VN> = OBRK.ODBC
                      IF ODBC.FILE AND ODBC.REC<VN> = '' THEN
                         ODBC.REC<VN> = OBRK.ODBC
                      END
                   END
                END ELSE
                  IF WIDTHS<1,VN> THEN TOT = TOT:SPACE(WIDTHS<1,VN>+SPC.CT)
                END
             END
          NEXT VN

          IF PRT.LINE THEN TOT = SPACE(PRT.LINE+1):TOT
          IF NOT(ODBC.FILE) OR COLUMN.OUT THEN
             PRINT TOT
             PRINT
          END

          RETURN
*-------------------------------------------------------------------------*
INIT: *** Initialize all variables for this routine as well as read the
      *** current WORK record for this report.

          UNIX.PID.GET PID$
          LINE.CT = 0
          DETAIL          = (OPT[1,1] = 'D')
          MASKS           = RAISE(MASKS)
          SS.MASKS        = RAISE(SS.MASKS)

          MASK.CT = DCOUNT(MASKS,AM)
          FOR S = 1 TO MASK.CT
             IF OCONV(MASKS<S>,"MCA") = "VD" THEN
                MASKS<S> = MASKS<S>[2,LEN(MASKS<S>)]
             END
          NEXT S

          CMD.LIST        = ''
          HDG             = ''
          FMTS            = ''
          BRK.POS         = ''
          TOTS            = ''
          BRK.TOT         = ''
          BRK.TOL         = ''
          BRK.COL         = ''
          GTOL            = ''
          DCODE           = ''
          PBK             = '@@'
          ICNT            = 0
          OLD.DICT.COMMON = ''
          LAST.PAGE       = NO
          ODBC.TOT.FLAG   = NO

          DIM WORK(60)
          OPEN 'REP.DESIGNS' TO REPFILE ELSE STOP
          READ NEW.WORK FROM  REPFILE,WORK.ID ELSE NEW.WORK = ''
          WORK.ITEM = RAISE(WORK.ITEM)
          MATPARSE WORK FROM WORK.ITEM

          RW.MSG = "R/W ":WORK.ID
          FLNM       = WORK(2)                ;* File to run this report
          SLCTS      = WORK(3)                ;* select statements to exec
          NEW.SLCTS  = NEW.WORK<3>
          DICTS      = WORK(4)                ;* Dictionary items
          TOTLS      = WORK(5)                ;* flag to total this dict
          WIDTHS     = WORK(6)                ;* column width
          ODBC.HDRS  = WORK(7)                ;* hdrs for cols w/o .....
          HEADERS    = WORK(7)                ;* heading for this column
          BREAKONS   = WORK(8)                ;* break on this column
          TITLES     = WORK(9)                ;* Title for this report
          RSLCT      = WORK(10)               ;* Reselect this report each
          PROMPTS    = WORK(11)               ;* Prompts for dict/selects
          VALS       = WORK(12)
          DVALS      = WORK(13)
          SPCS       = WORK(14)
          DCOMM      = WORK(15)
          CDLM       = WORK(16)
          RDLM       = WORK(17)
          TRIM.BLKS  = WORK(18)
          PRTLBL     = WORK(19)
          LBLSPECS   = WORK(20)
          PRT.PRMT   = WORK(21)
          ADD.PRMT   = WORK(22)
          DCOMM.PRMT = WORK(25)
          DICT.FMTS  = WORK(26)
          SS.PROMPTS = WORK(27)
          SS.VALS    = WORK(28)
          SS.DVALS   = WORK(29)
          SS.DATA    = WORK(30)
          NEW.TM     = NEW.WORK<34>           ;* last time select was done
          SAMP.CT    = WORK(33)               ;* number of samples in sel
          SEL.TM     = WORK(34)               ;* time the select was done
          SRT.DATA   = WORK(46)               ;* sort by information
          PRT.LINE   = WORK(47)
          REM.PATH   = RAISE(WORK(49)<1,1>)   ;* paths to remote dicts
          ODBC.FILE  = WORK(50)<1,1>          ;* ODBC Data source file ID
          ODBC.CLR   = WORK(50)<1,2>          ;* Clear the ODBC file or not
          ODBC.REL   = RAISE(WORK(50)<1,3>)   ;* ODBC Security information
          IF ODBC.FILE AND ODBC.FILE[1,4] # 'ODBC' THEN
             ODBC.FILE = 'ODBC.':ODBC.FILE
          END

          *** If the user didn't want us to re-select this report from the
          *** file then use the active lists (GET-LIST) instead.
          IF NOT(RSLCT) THEN
             IF SEL.TM # NEW.TM THEN
                RSLCT = YES
             END ELSE
                LC = 0
                FOR LL = 1 TO 5 UNTIL LC
                   IF SLCTS<1,(6-LL)> # '' THEN LC = 6 - LL
                NEXT LL
                IF LC < 1 THEN LC = 1; RSLCT = YES
                WORK(3)<1,11> = LC
             END
          END
          READ CTRL.REC FROM CTRLFILE,'PROMPT.CONTROL' ELSE CTRL.REC=''
          TOT.WIDTH  = WORK(31)
          IF CDLM THEN
             CLMSPC = CDLM
             SPC.CT = LEN(CDLM)
          END ELSE
             CLMSPC = ' '
             SPC.CT = 1
          END
          HDGSPC = ' '
          IF PRT.PRMT = '' THEN PRT.PRMT = 1
*-------------------------------------------------------------------------*
*--- Open the correct files *

          OPEN FLNM TO MAINFILE ELSE
             SEND.MESSAGE 'Phantom' , USER.ID,' Unable to open ',FLNM,' File - Program Aborted'
             STOP
          END

          *** Set up some variables to use for setting text fonts...
          SMALL      = CHAR(27):'(8U'
          SMALL     := CHAR(27):'(s0p16.67h8.5v0s0b0T'
          MED        = CHAR(27):"(8U":CHAR(27):'(s0p12h0s3b3T'
          SUP.SMALL  = CHAR(27):"(8U":CHAR(27):'(s0p20h0s0b0T'
          NORM       = CHAR(27):'(8U'
          NORM      := CHAR(27):'(s0p10h12v0s0b3T'
          NORM.BOLD  = CHAR(27):'(8U'
          NORM.BOLD := CHAR(27):'(s0p10h12v0s1b3T'
          LARGE      = CHAR(27):'(8U'
          LARGE     := CHAR(27):'(s1p10h14v0s3b3T'
          XLARGE     = CHAR(27):'(8U'
          XLARGE    := CHAR(27):'(s0p8h8v0s3b3T'
          SUP.SM.BLD = CHAR(27):"(8U":CHAR(27):'(s0p20h0s1b0T'

          * Update the last date/time that the report was run
          RW.SET.LAST.RUN.TIME WORK.ID,MAT WORK

          GOSUB LOAD.FORMATS

          RETURN
*-------------------------------------------------------------------------*
LOAD.FORMATS:*** Load the default formats for Windows Direct.
             *** Note: See REPORT.EXPORT and PC.COLUMNS for more info.
             ***
             *** FORMAT<col,1> - Column start position
             *** FORMAT<col,2> - Column length
             *** FORMAT<col,3> - Column data format: alpha/numeric (A/9)

          FORMATS = ''
          BEG.POS = 1
          CCNT    = DCOUNT(WIDTHS,VM)

          FOR CPOS = 1 TO CCNT
             FORMATS<1,CPOS,1> = BEG.POS
             FORMATS<1,CPOS,2> = WIDTHS<1,CPOS>

             * If totals is flagged '*' then char is alpha otherwise
             * it's numberic. Note: Totals is determined in Eclipse
             * Dictionary Maintenance by setting the Numberic option.
             IF TOTLS<1,CPOS> = '*' THEN
                FORMATS<1,CPOS,3> = 'A'
             END ELSE
                FORMATS<1,CPOS,3> = '9'
             END

             * Advance the next beginning position.
             BEG.POS = BEG.POS + WIDTHS<1,CPOS> + 1
          NEXT CPOS

          DRPT<54,8> = LOWER(FORMATS)

          RETURN
*-------------------------------------------------------------------------*
PERFORM.SELECT: * Select the items from the file based on the user specific
          ***  select information (Select screen of rep writer)
          SAVELIST.ID = WORK.ID
          PATH.INFO = WORK(49)
          SAVE.DICT.COMMON = DICT.COMMON

          PROMPT.CT = DCOUNT(SS.VALS,VM)
          SS.VALUS = ''
          FOR S = 1 TO PROMPT.CT
             IF SS.DATA<1,S> # '' THEN
                IF SS.VALS<1,S> = "$BRTERR$" THEN
                   BR.DATA = SS.DATA<1,S>
                   IF BR.DATA = 'All' THEN BR.DATA = 'ALL'

                   IF DCOUNT(BR.DATA,SSVM) > 1 THEN
                      *Already validated in SOLAR, just change to comma
                      *delimited
                      BRCHS          = RAISE(RAISE(BR.DATA))        ;*VM
                      SS.VALUS<1,S>  = RAISE(BR.DATA)               ;*SVM
                      BR.DATA        = CONVERT(SSVM,',',BR.DATA)    ;*,delimitted
                   END ELSE
                      VERIFY.BR BR.DATA,NAME,BRCHS,YES,YES
                      SS.VALUS<1,S>  = LOWER(BRCHS)
                   END

                   DICT.COMMON<1> = BRCHS
                   DICT.COMMON<2> = BR.DATA
                END ELSE
                   CONV.VAL = SS.DATA<1,S>
                   IF OCONV(SS.MASKS<S>,"MCA") = "D" AND NOT(NUM(CONV.VAL)) THEN
                      DATE.VAL = ICONV(CONV.VAL,"D")
                      IF DATE.VAL = '' THEN
                         DATE.VAL = REALDATE(CONV.VAL,DATE())
                      END
                      CONV.VAL = DATE.VAL
                   END
                   IF SS.DVALS<1,S,2> # '' THEN
                      DICT.COMMON<(SS.DVALS<1,S,2>)> = CONV.VAL
                   END
                   VAL.CT = DCOUNT(CONV.VAL,SVM)
                   FOR V = 1 TO VAL.CT
                      IF SS.MASKS<S> THEN CONV.VAL<1,1,V> = OCONV(CONV.VAL<1,1,V>,SS.MASKS<S>)
                   NEXT V
                   IF VAL.CT > 1 THEN
                      CONVERT SVM TO ";" IN CONV.VAL
                   END
                   SS.VALUS<1,S>  = CONV.VAL
                END
             END
          NEXT S

          IF INDEX(TITLES,"$",1) OR INDEX(TITLES,"!",1) THEN
             REP.SEL.REPL TITLES,SS.VALS,SS.VALUS
          END

          IF DRPT<33> = '' THEN
             TITLE    = (TITLES<1,1>:" ":TITLES<1,2>)'L#46'
             IF RDLM THEN TITLE = "** ":TITLE
          END ELSE
             TITLE = DRPT<33>
          END

          IF NOT(RSLCT) THEN RETURN

          LC        = 1
          SLCT.PLUS = ''
          MSG = 'Selecting ':TITLE
          GOSUB WRT.STAT
          REP.SLCT.PLUS FLNM,SLCT.PLUS,WORK.ID

          SET.DATA = YES
          FOR X = 1 TO 5
             SEL.CMD = SLCTS<1,X>
             IF SEL.CMD THEN
                IF SET.DATA THEN
                   SET.DATA = NO
                   IF SLCT.PLUS THEN
                      RW.EXECUTE.CMD FLNM,SLCT.PLUS,SAVELIST.ID,10,X,PATH.INFO,ERR.MSG
                   END
                END
                REP.SEL.REPL SEL.CMD,SS.VALS,SS.VALUS,YES
                CMD.LIST<LC> = SEL.CMD
                RW.EXECUTE.CMD FLNM,SEL.CMD,SAVELIST.ID,LC,X,PATH.INFO,ERR.MSG
                IF ERR.MSG THEN
                   SEND.MESSAGE 'Phantom' ,USER.ID,ERR.MSG
                   STOP
                END ELSE
                   WORK(3)<1,11> = LC
                   LC = LC + 1
                   EXECUTE 'DELETE-LIST ':SAVELIST.ID:'~':LC:PID$ CAPTURING DMY
                END
             END
          NEXT X

* RESET DICT.COMMON AFTER SELECTIONS
          RETURN
*-------------------------------------------------------------------------*
GET.DICT: *
          Q = DCOUNT(DVALS,VM)
          FOR S = 1 TO Q
          DICT.POS = DVALS<1,S,2>
          IF DICT.POS THEN
             BEGIN CASE
             CASE DICT.POS = 2
                BR.DATA = VALUS<1,S>
                IF BR.DATA = 'All' THEN BR.DATA = 'ALL'

                IF DCOUNT(BR.DATA,SSVM) > 1 THEN
                   *Already validated in SOLAR, just change to comma
                   *delimited
                   BRCHS       = RAISE(RAISE(BR.DATA))        ;*VM
                   VALUS<1,S>  = RAISE(BR.DATA)               ;*SVM
                   BR.DATA     = CONVERT(SSVM,',',BR.DATA)    ;*,delimitted
                END ELSE
                   VERIFY.BR BR.DATA,NAME,BRCHS,YES,YES
                   VALUS<1,S>  = LOWER(BRCHS)
                END

                DICT.COMMON<1> = BRCHS
                DICT.COMMON<2> = BR.DATA
             CASE DICT.POS = 3 OR DICT.POS = 4
                CONV.VAL = VALUS<1,S>
                IF NUM(CONV.VAL) THEN
                   DATE.VAL = CONV.VAL
                END ELSE
                   DATE.VAL = ICONV(CONV.VAL,"D")
                   IF DATE.VAL = '' THEN
                      DATE.VAL = REALDATE(CONV.VAL,DATE())
                   END
                   CONV.VAL = DATE.VAL
                END
                DICT.COMMON<DICT.POS> = DATE.VAL
                VALUS<1,S> = OCONV(DATE.VAL,MASKS<S>)
             CASE 1
                DICT.COMMON<DICT.POS> = VALUS<1,S>
             END CASE
          END ELSE
             CONV.VAL = VALUS<1,S>
             IF OCONV(MASKS<S>,"MCA") = "D" AND NOT(NUM(CONV.VAL)) THEN
                DATE.VAL = ICONV(CONV.VAL,"D")
                IF DATE.VAL = '' THEN
                   DATE.VAL = REALDATE(CONV.VAL,DATE())
                END
                CONV.VAL = DATE.VAL
             END
             VAL.CT = DCOUNT(CONV.VAL,SVM)
             FOR V = 1 TO VAL.CT
                IF MASKS<S> THEN CONV.VAL<1,1,V> = OCONV(CONV.VAL<1,1,V>,MASKS<S>)
             NEXT V
             IF VAL.CT > 1 THEN
                CONVERT SVM TO ";" IN CONV.VAL
             END
             VALUS<1,S>  = CONV.VAL
          END
          NEXT S
          OLD.DICT.COMMON = DICT.COMMON

*--- Set formats for Dict items and determine Heading length *
***********************************************************
          TOT.CNT  = 0
          TOTLN    = ''
          COLEN    = TOT.WIDTH
          DCONV    = ''
          DICT.COM = ''
          FORMULAS = ''
*         FTOT.VAL = ''
          DCMCNT = DCOUNT(CTRL.REC,AM)
          VAL.NUM = DCOUNT(DICTS<1>,VM)
          FOR Y   = 1 TO VAL.NUM
             DICTID = FIELD(DICTS<1,Y>,',',1)
             TRUE.FILE = ''
             TRUE.DICT = ''
             RW.PARSE.DICT.ID DICTID,FLNM,TRUE.FILE,TRUE.DICT
             READ DICTREC FROM EDICTFILE,TRUE.FILE:"~":TRUE.DICT THEN
*---Dictionary Item
                DCODE<1,Y>    = DICTREC<1>
                MSK  = DICTREC<7>
                JST  = DICTREC<9>
                IF JST = '' THEN JST = 'L'
                IF MSK[1,2]="MR" THEN
                   DCONV<1,Y> = MSK
                   JST  = 'R'
                END
             END ELSE
*---Formula
                JST  = 'R'
                FORMULA = DICTS<1,Y>
                REP.VERF.FORMULA FORMULA,FORM.ERR,4,Y
                FORMULAS<1,Y>=FORMULA
                MSK  = DICT.FMTS<1,Y>
             END
             IF DICT.FMTS<1,Y> = '' AND MSK # '' THEN
                DICT.FMTS<1,Y>  = MSK
             END
             FMTS<1,-1> = JST:'#':WIDTHS<1,Y>
             IF TOTLS<1,Y> = '1' OR TOTLS<1,Y> = '2' THEN
                IF WIDTHS<1,Y> THEN TOTLN = TOTLN:STR('=',WIDTHS<1,Y>):CLMSPC
                TOT.CNT += 1
             END ELSE
                IF WIDTHS<1,Y> THEN TOTLN = TOTLN:SPACE(WIDTHS<1,Y>+SPC.CT)
             END

             IF BREAKONS<1,Y> THEN
                BRK.POS<1,-1> = Y
             END
             FOR DCMV = 1 TO DCMCNT
                IF DCOMM<1,Y,DCMV> # '' THEN
                   DICT.COM<Y,DCMV>=RAISE(DCOMM<1,Y,DCMV>)
                   IF DCMV = 3 OR DCMV = 4 THEN
                      CONV.VAL = DICT.COM<Y,DCMV>
                      IF NOT(NUM(CONV.VAL)) THEN
                         DATE.VAL = ICONV(CONV.VAL,"D")
                         IF DATE.VAL = '' THEN
                            DATE.VAL = REALDATE(CONV.VAL,DATE())
                         END
                         CONV.VAL = DATE.VAL
                      END
                      DICT.COM<Y,DCMV> = CONV.VAL
                   END
                END ELSE
                   DICT.COM<Y,DCMV>= LOWER(DICT.COMMON<DCMV>)
                END
             NEXT DCMV
          NEXT Y

          IF PRT.LINE THEN TOTLN = SPACE(PRT.LINE+1):TOTLN
          FULLEN  = LEN(TOTLN)
          BRK.CNT = DCOUNT(BRK.POS,VM)

          RETURN
*-------------------------------------------------------------------------*
SET.HEADER: *
          IF COLEN < 80 THEN COLEN = 80



*-----Title
          SPC1     = (COLEN - LEN(TITLES<1,1>)) / 2
          HDG<1,1> = SPACE(SPC1):TITLES<1,1>:SPACE(SPC1-12):'PAGE:^#####'
          IF TITLES<1,2> THEN
             SPC2     = (COLEN - LEN(TITLES<1,2>)) / 2
             HDG<1,2> = SPACE(SPC2):TITLES<1,2>
             HDL      = 3
          END ELSE
             HDL      = 2
          END


          * see if any links to remote dictionaries are broken
          PATH.ERRS     = ''
          RW.VALIDATE.FILE.PATH MAT WORK,,PATH.ERRS
          IF PATH.ERRS THEN
             * Append broken links to report header
             HDG = INSERT(HDG,1,HDL;VM:LOWER(PATH.ERRS))
             HDL = DCOUNT(HDG<1>,VM)+1
          END

          HDLN     = 0
*-----Prompts
          IF PRT.PRMT THEN
             HDL = HDL+1
             HDG<1,HDL> = STR('*',COLEN)
             HDL = HDL+1
             HDG<1,HDL> = '* Report ID : ':WORK.ID:' '
             R = DCOUNT(PROMPTS,VM)
             FOR N = 1 TO R
                HVAL  = VALUS<1,N>
                IF HVAL # '' THEN
                   IF COUNT(HVAL,SVM) THEN
                      CONVERT SVM TO ',' IN HVAL
                      COMMA.COMPRESS HVAL,HVAL
                   END
                   IF OCONV(MASKS<N>,"MCA")[1,2] = "YN" THEN
                      NXTP = PROMPTS<1,N>:" ":'NY'[(HVAL+1),1]
                   END ELSE
                      NXTP = PROMPTS<1,N>:" ":OCONV(HVAL,MASKS<N>)
                   END
                   IF HDLN + LEN(NXTP) GT COLEN THEN
                      HDL        = HDL+1
                      HDG<1,HDL> = '* '
                      HDLN       = 0
                   END
                   IF HDLN  <= 2 THEN SSCS = '' ELSE SSCS = " ; "
                   HDG<1,HDL> = HDG<1,HDL>:SSCS:NXTP
                   HDLN       = LEN(HDG<1,HDL>)
                END
             NEXT N

             R = DCOUNT(SS.PROMPTS,VM)
             IF RSLCT THEN
                SEL.MSG = TIMEDATE()
             END ELSE
                SEL.MSG = SEL.TM
             END

             IF SAMP.CT THEN
                SAMP.MSG = "  Sample ":SAMP.CT
             END ELSE
                SAMP.MSG = ''
             END

             HDL        = HDL+1
             HDG<1,HDL> =  "* Selected  : ": SEL.MSG : SAMP.MSG
             HDLN       = 2
             HDL        = HDL+1
             HDG<1,HDL> = '* '

             FOR N = 1 TO R
                HVAL  = SS.DATA<1,N>
                IF HVAL # '' THEN
                   HVAL  = SS.VALUS<1,N>
                   IF COUNT(HVAL,SVM) THEN
                      CONVERT SVM TO ',' IN HVAL
                      COMMA.COMPRESS HVAL,HVAL
                   END
                   CONVERT ';' TO ',' IN HVAL
                   IF OCONV(SS.MASKS<N>,"MCA")[1,2] = "YN" THEN
                      NXTP  = SS.PROMPTS<1,N>:" ":'NY'[(HVAL+1),1]
                   END ELSE
                      NXTP  = SS.PROMPTS<1,N>:" ":HVAL
                   END
                   IF HDLN + LEN(NXTP) GT COLEN THEN
                      HDL        = HDL+1
                      HDG<1,HDL> = '* '
                      HDLN       = 0
                   END
                   IF HDLN <= 2 THEN SSCS = '' ELSE SSCS = " ; "
                   HDG<1,HDL> = HDG<1,HDL>:SSCS:NXTP
                   HDLN       = LEN(HDG<1,HDL>)
                END
             NEXT N

             HDL = HDL+1
             HDG<1,HDL> = STR('*',COLEN)
             HDL = HDL+2
          END



*** Column headings
          TRY2 = NO
          GOSUB SET.COL.HDGS



          MSG = 'Spooling ':TITLE
          GOSUB WRT.STAT
          LC = WORK(3)<1,11>

          RETURN
*-------------------------------------------------------------------------*
SET.COL.HDGS: *** Setup the heading for each column on the report.

          IF INDEX(HEADERS,"$",1) AND SS.VALUS # '' THEN
             REP.SEL.REPL HEADERS,SS.VALS,SS.VALUS
          END

          *** Report logic for column headings
          NLNS  = 1
          FOR X = 1 TO VAL.NUM
             IF LEN(HEADERS<1,X>) GT WIDTHS<1,X> AND WIDTHS<1,X> AND HEADERS<1,X,2> = '' THEN
                FOLD HEADERS<1,X>,WIDTHS<1,X>,HDRS,NL
                HDRS = LOWER(HDRS)
                HEADERS<1,X> = HDRS
             END
             IF HEADERS<1,X,2> # '' THEN
                NL = DCOUNT(HEADERS<1,X>,SVM)
                IF NL GT NLNS THEN NLNS = NL
             END
          NEXT X

          FOR X = 1 TO VAL.NUM
             HDDL = HDL
             FOR NLS = 1 TO NLNS
                IF WIDTHS<1,X> THEN
                   *** Only add to the header if the column has width.
                   IF HEADERS<1,X,NLS> NE '' THEN
                      HEADERS<1,X,NLS> = HEADERS<1,X,NLS>:STR(" ",WIDTHS<1,X>)
                      HEADERS<1,X,NLS> = HEADERS<1,X,NLS>[1,WIDTHS<1,X>]
                   END ELSE
                      HEADERS<1,X,NLS> = STR(" ",WIDTHS<1,X>)
                   END
                   HDG<1,HDDL>  = HDG<1,HDDL>:HEADERS<1,X,NLS>:HDGSPC
                END

                IF PRT.LINE AND X = 1 THEN
                   IF NLS = 1 THEN
                      TMP.FMT = "L#":PRT.LINE
                      TMP.WID = PRT.LINE - 5
                      IF TMP.WID < 0 THEN TMP.WID = 0
                      TMP.PRT = "Line#":STR('.',TMP.WID)
                      HDG<1,HDDL> = TMP.PRT TMP.FMT:' ':HDG<1,HDDL>
                   END ELSE
                      HDG<1,HDDL> = SPACE(PRT.LINE+1):HDG<1,HDDL>
                   END
                END

                HDDL+=1
             NEXT NLS
          NEXT X

          IF WORK.ID = "GPS.EXP.REPORT" OR WORK.ID = "GPS.AD.CHK.REPORT" OR WORK.ID="GPS.DRIVER.REPORT" THEN
          *LASTLINE = DCOUNT(HEADERS,VM)
          CONVERT VM TO " " IN HEADERS
          HDG = HEADERS
          *HDG = HEADERS<1,X,NLS>:STR(" ",WIDTHS<1,X>)
          END

          IF WORK.ID="GPS.TICKET.LOG" THEN
          CONVERT VM TO "," IN HEADERS
          HDG = HEADERS
          END

          IF NOT(ODBC.FILE) OR COLUMN.OUT THEN
             PRINTER.ON COLEN,TITLE,DOC.ID,HDG,RPT.DFLT=DRPT
          END
          IF NOT(ODBC.FILE) AND COLUMN.OUT THEN ODBC.FILE = 'ODBC.':DOC.ID

          IF ODBC.FILE THEN
             *** this routine will go out and create the needed dict items
             *** on the ODBC file for retrieving the data at a later time
RETRY:       UT.OPEN.FILE ODBC.FILE,ODBCFILE,ERR.MSG,YES

             BEGIN CASE
             CASE ERR.MSG AND NOT(TRY2)
                EXECUTE "CREATE-FILE ":ODBC.FILE:" 1 500,4,30" CAPTURING MG
                TRY2 = YES
                GOTO RETRY
             CASE ERR.MSG
                SEND.MESSAGE 'Phantom',USER.ID,TITLE:' ':ERR.MSG
                STOP
             END CASE

             IF COLUMN.OUT THEN
                WORK.ITEM<50> = ODBC.FILE:VM:YES:VM:YES
             END

             ODBC.SET.DICTS ODBC.FILE,WORK.ITEM
             RETURN
          END

          RETURN
*-------------------------------------------------------------------------*
GET.LST: * Get list of appropriate items based on the select criteria
          IF RSLCT THEN
             GET.LID = SAVELIST.ID:'~':LC:PID$ ;* get.list ID
          END ELSE
             GET.LID = SAVELIST.ID:'~':LC
          END
          GL.EXEC = "GET-LIST ":GET.LID
          EXECUTE GL.EXEC CAPTURING MSG

          IF SYSTEM(11) = 0 THEN
             RETURN TO FINISH
          END

          COMMAND = "SELECT ":FLNM:" TO 7"
          IF SAMP.CT > 0 THEN
             COMMAND := " (":SAMP.CT
          END
          EXECUTE COMMAND PASSLIST CAPTURING MSG

          RETURN
*-------------------------------------------------------------------------*
PRT.LBLS: LISTNAME = SAVELIST.ID:'~':WORK(3)<1,11>
          IF RSLCT THEN
             LISTNAME := PID$
          END

          GL.EXEC  = "GET-LIST ":LISTNAME
          EXECUTE GL.EXEC CAPTURING MSG
          IF SYSTEM(11) = 0 THEN
             SEND.MESSAGE 'Phantom',USER.ID,TITLE:' has selected 0 items - No labels produced'
             STOP
          END

          IF SAMP.CT > 0 THEN
             COMMAND = "SELECT ":FLNM:" (":SAMP.CT
             EXECUTE COMMAND PASSLIST CAPTURING MSG
          END

          CNT     = LBLSPECS<1,1>
          SIZ     = LBLSPECS<1,5>
          SPAC    = LBLSPECS<1,6>
          COLEN   = (CNT * (SIZ + SPAC))
          IF CNT > 1 THEN COLEN = COLEN - SPAC
          IF COLEN < 80 THEN COLEN = 80
* Sent the Auto formfeed to disabled
          DRPT<32> = YES

          PRINTER.ON COLEN,TITLE,DOC.ID,RPT.DFLT=DRPT

          *** send the formats to REP.PHR.LABELS
          DICTS = DICTS:AM:DICT.FMTS:AM:REM.PATH

          REP.PHR.LABELS FLNM,LBLSPECS,DICT.COM,DICTS,VAL.NUM,WIDTHS,MAINFILE,DICTFILE,HEADERS

          PRINTER.OFF DOC.ID
          SEND.MESSAGE 'Phantom',USER.ID,TITLE:' is Complete'

          RETURN
*-------------------------------------------------------------------------*
CHK.CONV: *** Put any dates or numeric fields for ODBC files into internal
          *** format so SELECT and SSELECT will work correctly.
          FOR CC = 1 TO CONV.CT
             *** OCONV both sets of values
             TVAL = OCONV(TVAL,DICT.CONV<1,CC>)
             TVAL.ODBC = OCONV(TVAL.ODBC,DICT.CONV<1,CC>)

             IF ODBC.FILE THEN
                *** ICONV the ODBC values but NOT the others
                IF DICT.CONV<1,CC>[1,2]='MR' OR DICT.CONV<1,CC>[1,1]='D' THEN
                   TVAL.ODBC = ICONV(TVAL.ODBC,DICT.CONV<1,CC>)
                END
             END
          NEXT CC

          RETURN
*-------------------------------------------------------------------------*
WRT.STAT: *** Write out the status of the report on the phantom status file
          MSG<2> = RW.MSG
          MSG<3> = USER.ID

          WRITE MSG ON PHSTFILE,PID$

          RETURN
*-------------------------------------------------------------------------*
!TSMITH~05/17/17~14:32
